<template>
  <div class="fx-form-item" :class="[labelPosition]">
    <span :style="{width:`${labelWidth}px`}" v-if="showLabel" v-tooltip="label" fx-tooltip-text-over>{{ label }}</span>
    <div class="fx-form-item-content">
      <slot></slot>
      <transition name="fade">
        <div class="rules-error" v-if="errorMessage!==''">
          {{ errorMessage }}
        </div>
      </transition>
    </div>
  </div>
</template>

<script lang="ts">
import {
  defineComponent,
  inject,
  onBeforeMount,
  onBeforeUnmount,
  onMounted,
  PropType,
  provide,
  reactive,
  ref,
  Ref
} from 'vue'
import { LabelPosition, RulesProps } from '@/base/form/formTypes'
import mitt, { Emitter } from 'mitt'
import { formItemRules } from '@/base/form/form.vue'

interface FormItemProps {
  label: string,
  prop: string,
  showLabel: boolean,
}

export type FormValidatorFun = () => Promise<boolean>

export default defineComponent({
  name: 'formItem',
  props: {
    // 表头
    label: {
      type: String as PropType<string>,
      default: ''
    },
    // 参数
    prop: {
      type: String as PropType<string>,
      default: ''
    },
    // 是否需要 label
    showLabel: {
      type: Boolean as PropType<boolean>,
      default: true
    }
  },
  setup (props: FormItemProps) {
    // 验证错误信息
    const errorMessage = ref<string>('')
    // 定义formItem监听发给插槽
    const formItemMitt: Emitter = mitt()
    // 验证信息
    const validatorInfo = reactive<{ val: string | string[] | number, trigger: 'blur' }>({
      val: '',
      trigger: 'blur'
    })
    // 接收label宽度
    const labelWidth = inject<string | number>('labelWidth')
    // 接收label位置
    const labelPosition = inject<LabelPosition>('labelPosition')
    // 接收所有验证规则
    const rules = inject<Ref<{ [propName: string]: formItemRules }> | undefined>('formRules', undefined)
    // 传递formItem传递方法
    provide<Emitter['emit']>('formItemMitt', formItemMitt.emit)
    // 验证方法
    const handleValidator: FormValidatorFun = async () => {
      const val = validatorInfo.val
      const trigger = validatorInfo.trigger
      let isValidatedAll = true
      if (rules) {
        let rule = rules.value[props.prop]
        rule = rule instanceof Array ? rule : [rule] as RulesProps[]
        for (let i = 0; i < rule.length; i++) {
          const item = rule[i]
          if (!item) break
          if (item.required) {
            if ((item.type === 'Array' && val instanceof Array && val.length === 0) || val === '') {
              isValidatedAll = false
              errorMessage.value = item.message
              break
            }
            if (item.type === 'Boolean' && !val) {
              isValidatedAll = false
              errorMessage.value = item.message
              break
            }
          }
          if (trigger === 'blur' && item.trigger === 'blur') {
            if (item.rules && typeof val === 'string') {
              isValidatedAll = item.rules.test(val)
              errorMessage.value = item.message
              break
            }
            if (typeof val !== 'number' && ((item.min && val.length < item.min) || (item.max && val.length > item.max))) {
              isValidatedAll = false
              errorMessage.value = item.message
              break
            }
            if (item.validator) {
              await item.validator(val, (validator: string) => {
                if (!validator) return
                if (validator !== '') {
                  errorMessage.value = validator
                }
                isValidatedAll = validator === ''
              })
            }
          }
          if (item.type === 'Array' && val instanceof Array) {
            if ((item.min && val.length < item.min) || (item.max && val.length > item.max)) {
              isValidatedAll = false
              errorMessage.value = item.message
              break
            }
          }
          if (item.type === 'Number' && Number.isInteger(val)) {
            if ((item.min && val < item.min) || (item.max && val > item.max)) {
              isValidatedAll = false
              errorMessage.value = item.message
              break
            }
          }
        }
        if (isValidatedAll) errorMessage.value = ''
      }
      return isValidatedAll
    }
    // 获取formItem下的数据 方便验证
    const handleFormItemValue = (val: string | undefined): void => {
      if (val !== undefined) validatorInfo.val = val
    }
    // 失焦调用验证
    const handleFormItemBlur = (val: string | undefined): void => {
      if (val !== undefined) {
        validatorInfo.val = val
        validatorInfo.trigger = 'blur'
        handleValidator()
      }
    }

    onMounted(() => {
      // 获取form的监听函数
      const formMittEmit = inject<Emitter['emit']>('formMittEmit')
      // 发送监听方法到form
      if (formMittEmit && props.prop !== '') {
        formMittEmit('formValidator', {
          prop: props.prop,
          fun: handleValidator
        })
      }
    })

    onBeforeMount(() => {
      formItemMitt.on<string | undefined>('formItemBlur', handleFormItemBlur)
      formItemMitt.on<string | undefined>('formItemValue', handleFormItemValue)
    })

    onBeforeUnmount(() => {
      formItemMitt.off('formItemBlur', handleFormItemBlur)
      formItemMitt.off('formItemValue', handleFormItemValue)
    })

    return {
      labelWidth,
      errorMessage,
      labelPosition,
      handleValidator
    }
  }
})
</script>
